Kodowanie: Windows-1250, koniec wiersza: CR+LF, test: zażółć gęślą jaźń Autor: Adam Sawicki, e-mail: regedit@regedit.gamedev__USUN__.pl http://www-unix.mcs.anl.gov/mpi/ oficjalna strona główna http://draco.icis.pcz.pl:8080/~wiki/ wiki pana od laborki http://icis.pcz.pl/~draco/ strona pana od laborki http://icis.pcz.pl/~draco/mpi/ tutorial MPI na stronie pana od laborki Jak kodzić? ================================================================================ #include Kompilacja: mpiCC -o Program Program.cpp Uruchamianie: mpirun -np 5 Program Gdzie: Program - nazwa programu 5 - liczba procesów do uruchomienia równolegle Jeśli trzeba jeszcze zainicjalizować (raczej nie trzeba): lamboot Pojęcia ================================================================================ Size Liczba procesów w komunikatorze Rank Numer (indeks) danego procesu w komunikatorze, z zakresu 0..Size-1 Communicator Grupa procesów (chociaż w MPI grupa to jest jeszcze osobne pojęcie) Na starcie instnieje MPI_COMM_WORLD, można tworzyć własne. Tag Numer identyfikacyjny nadawany wysyłanemu komunikatowi żeby np. odróżnić różne rodzaje komunikatów - do własnego użytku programisty. Tryby komunikacji: - Blokujący - funkcja zwraca sterowanie dopiero jak operacja się zakończy, jakkolwiek ta operacja nie wygląda - Nieblokujący (I - Immediate) - funkcja zwraca sterowanie natychmiast, operacja wykonuje się w tle, potem można odpytać o wynik - Normalny - Synchroniczny (S) Funkcja wysyłająca zwraca sterowanie dopiero kiedy wywołana zostanie u odbiorcy funkcja odbierająca. - Buforowany (B) Funkcja wysyłająca zwraca sterowanie natychmiast, dane są buforowane. - Przygotowany (R) Funkcja wysyłająca wymaga aby odbiorca wywołał funkcję odbierającą już wcześniej - inaczej jest błąd. Typy ================================================================================ MPI_Status: Status odbioru komunikatu - zwraca go Recv, a także funkcje typu Wait i Test MPI_Datatype Typ danych MPI_Comm Komunikator MPI_Group Grupa MPI_Op Rodzaj operacji obliczeniowej MPI_Request Żądanie nieblokujące Stałe ================================================================================ MPI_SUCCESS Zwracają funkcje MPI jeśli jest OK MPI_COMM_WORLD Główny komunikator obejmujący wszystkie procesy, istnieje na starcie MPI_COMM_NULL Komunikator pusty - otrzymuje go proces który nie zmieścił się w tworzonym komunikatorze MPI_ANY_SOURCE Do funkcji Recv - czekamy na komunikat od dowolnego nadawcy MPI_ANY_TAG Do funkcji Recv - czekamy na komunikat o dowolnym tagu MPI_UNDEFINED ??? Typy danych ================================================================================ MPI_CHAR MPI_SHORT MPI_INT MPI_LONG MPI_UNSIGNED_CHAR MPI_UNSIGNED_SHORT MPI_UNSIGNED MPI_UNSIGNED_LONG MPI_FLOAT MPI_DOUBLE MPI_LONG_DOUBLE MPI_BYTE MPI_PACKED - dane przetworzone przez MPI_Pack/MPI_Unpack Funkcje do obliczeń ================================================================================ MPI_MAX - maksimum MPI_MIN - minimum MPI_SUM - suma MPI_PROD - iloczyn (od ang. product) MPI_LAND - logiczne and MPI_LOR - logiczne or MPI_LXOR - logiczne xor MPI_BAND - binarne and MPI_BOR - binarne or MPI_BXOR - binarne xor MPI_MAXLOC - maximum value and location (???) MPI_MINLOC - minimum value and location (???) Funkcje ================================================================================ int MPI_Init(int *argc, char ***argv) Inicjalizacja MPI - na początku int MPI_Finalize() Finalizacja MPI - na końcu int MPI_Initialized(int *flag) Czy MPI jest zainicjalizowane? int MPI_Abort(MPI_Comm comm, int errorcode) Przerywa wszystkie procesy skojarzone z komunikatorem int MPI_Get_processor_name(char *name, int *resultlen) Nazwa procesora dla bieżącego procesu double MPI_Wtime(void) Czas w sekundach od jakiegoś punktu 0 double MPI_Wtick(void) Czas w sekundach między impulsami zegara int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count) ??? Tryb blokujący -------------- int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status) int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, MPI_Datatype recvtag, MPI_Comm comm, MPI_Status *status) Tryb synchroniczny (S): int MPI_Ssend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Tryb buforowany (B): int MPI_Bsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) int MPI_Buffer_attach(void* buffer, int size) Ustala własny bufor użytkownika int MPI_Buffer_detach(void* buffer, int* size) Wyrejestrowuje bufor użytkownika Tryb przygotowany (R): int MPI_Rsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) Tryb nieblokujący (I jak Immediate) ----------------- int MPI_Isend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request) Nie zwalniać bufora aż do upewnienia się że już wysłało/odebrało! Tryb synchroniczny (IS): int MPI_Issend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) Tryb buforowany (IB): int MPI_Ibsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) Tryb przygotowany (IR): int MPI_Irsend(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) int MPI_Wait(MPI_Request *request, MPI_Status *status) int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status) int MPI_Waitany(int count, MPI_Request *array_of_requests, int *index, MPI_Status *status) int MPI_Testany(int count, MPI_Request *array_of_requests, int *index, int *flag, MPI_Status *status) int MPI_Waitall(int count, MPI_Request *array_of_requests, MPI_Status *array_of_statuses) int MPI_Testall(int count, MPI_Request *array_of_requests, int *flag, MPI_Status *array_of_statuses) int MPI_Waitsome(int incount, MPI_Request *array_of_requests, int *outcount, int *array_of_indices, MPI_Status *array_of_statuses) int MPI_Testsome(int incount, MPI_Request *array_of_requests, int *outcount, int *array_of_indices, MPI_Status *array_of_statuses) int MPI_Cancel(MPI_Request *request) int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag, MPI_Status *status) int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status) Operacje grupowe ---------------- int MPI_Barrier(MPI_Comm comm) void MPI_User_function( void *invec, void *inoutvec, int *len, MPI_Datatype *datatype) Nieliczące: int MPI_Bcast(void* buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm) int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm) int MPI_Allgather(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm) int MPI_Scatter(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm) int MPI_Alltoall(void* sendbuf, int sendcount, MPI_Datatype sendtype, void* recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm) Liczące: int MPI_Reduce(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm) int MPI_Allreduce(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) int MPI_Reduce_scatter(void* sendbuf, void* recvbuf, int *recvcounts, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) int MPI_Scan(void* sendbuf, void* recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm) int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op) int MPI_Op_free(MPI_Op *op) Własne typy ----------- int MPI_Type_contiguous(int count, MPI_Datatype oldtype, MPI_Datatype *newtype) Typ tablicowy int MPI_Type_vector(int count, int blocklength, int stride, MPI_Datatype oldtype, MPI_Datatype *newtype) int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride, MPI_Datatype oldtype, MPI_Datatype *newtype) Typ macierzowy - blocklength kolumn, count wierszy stride - odstępy między wierszami: - MPI_Type_vector - w elementach - MPI_Type_hvector - w bajtach int MPI_Type_indexed(int count, int *array_of_blocklengths, int *array_of_displacements, MPI_Datatype oldtype, MPI_Datatype *newtype) int MPI_Type_hindexed(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements, MPI_Datatype oldtype, MPI_Datatype *newtype) Typ macierzowy, różna liczba kolumn w różnych wierszach int MPI_Type_struct(int count, int *array_of_blocklengths, MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types, MPI_Datatype *newtype) Typ własnej struktury int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent) Zwraca rozmiar podanego typu danych w bajtach int MPI_Type_commit(MPI_Datatype *datatype) Zatwierdza w systemie typ danych Grupy ----- int MPI_Comm_group(MPI_Comm comm, MPI_Group *group) zwraca grupę związaną z danym komunikatorem int MPI_Group_size(MPI_Group group, int *size) zwraca liczbę procesów w podanej grupie int MPI_Group_rank(MPI_Group group, int *rank) zwraca numer bieżącego procesu w podanej grupie lub MPI_UNDEFINED jeśli nie należy int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup) tworzy grupę będącą sumą procesów z podanych dwóch grup int MPI_Group_intersection(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup) tworzy grupę będącą częścią wspólną procesów z podanych dwóch grup int MPI_Group_difference(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup) tworzy grupę z procesami, które należą do pierwszej podanej grupy i nie należą do drugiej int MPI_Group_incl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup) tworzy grupę zawierającą n procesów z podanej grupy o numerach podanych w tablicy ranks int MPI_Group_excl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup) tworzy grupę zawierającą procesy z podanej grupy z wyjątkiem n procesów o numerach podanych w tablicy ranks int MPI_Group_free(MPI_Group *group) zwalnia grupę (zmniejsza licznik referencji) int MPI_Group_compare(MPI_Group group1, MPI_Group group2, int *result) porównuje dwie grupy czy zawierają te same procesy i czy w tej samej kolejności Komunikatory ------------ int MPI_Comm_rank(MPI_Comm comm, int *rank) zwraca numer bieżącego procesu w danym komunikatorze int MPI_Comm_size(MPI_Comm comm, int *size) zwraca liczbę procesów w danym komunikatorze int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm) tworzy komunikator jako kopię podanego komunikatora int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm) tworzy komunikator na podstawie grupy int MPI_Comm_free(MPI_Comm *comm) zwalnia komunikator (zmniejsza licznik referencji) int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int *result) porównuje dwa komunikatory Topologie --------- int MPI_Topo_test(MPI_Comm comm, int *status) zwraca typ podanej topologii int MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims, int *periods, int reorder, MPI_Comm *comm_cart) tworzy topologię kartezjańską int MPI_Dims_create(int nnodes, int ndims, int *dims) wylicza zalecaną liczbę węzłów w poszczególnych wymiarach dla topologii kartezjańskiej (chyba...) int MPI_Cart_coords(MPI_Comm comm, int rank, int maxdims, int *coords) zwraca współrzędne kartezjańskie węzła o podanym numerze w podanej topologii kartezjańskiej int MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods, int *coords) zwraca informacje dotyczące podanej topologii kartezjańskiej int MPI_Cart_map(MPI_Comm comm, int ndims, int *dims, int *periods, int *newrank) mapuje procesy (to znaczy???) int MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank) zwraca numer procesu o podanych współrzędnych kartezjańskich int MPI_Cart_shift(MPI_Comm comm, int direction, int disp, int *rank_source, int *rank_dest) cykliczne przesunięcie w topologii kartezjańskiej (przenumerowanie procesów???) int MPI_Cart_sub(MPI_Comm comm, int *remain_dims, MPI_Comm *newcomm) tworzy nowy komunikator dzieląc podaną topologię kartezjańską (np. tylko kolumna z macierzy) int MPI_Cartdim_get(MPI_Comm comm, int *ndims) zwraca liczbę wymiarów podanej topologii kartezjańskiej int MPI_Graph_create(MPI_Comm comm_old, int nnodes, int *index, int *edges, int reorder, MPI_Comm *comm_graph) tworzy topologię grafową int MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges, int *index, int *edges) int MPI_Graphdims_get(MPI_Comm comm, int *nnodes, int *nedges) zwraca informacje o podanej topologii grafowej int MPI_Graph_map(MPI_Comm comm, int nnodes, int *index, int *edges, int *newrank) mapuje procesy w topologii grafowej (???) int MPI_Graph_neighbors_count(MPI_Comm comm, int rank, int *nneighbors) int MPI_Graph_neighbors(MPI_Comm comm, int rank, int maxneighbors, int *neighbors) pobiera liczbę/listę sąsiadów podanego węzła grafu